package com.spooky.esb;

import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import javax.activation.UnsupportedDataTypeException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.spooky.esb.exception.MissingAnnotationException;
import com.spooky.esb.share.annotation.component.Parameter;

public class ComponentToXSDTransformer {
	private static final String NAMESPACE_ALIAS = "xs";
	private static final String NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema";

	public static void main(String[] args) throws Exception {
		Method[] methods = TestComponent.class.getDeclaredMethods();
		Document document = XmlUtil.createDocument("1.0", "UTF-8");
		Element root = XmlUtil.createRootNamespace(document, NAMESPACE_ALIAS, NAMESPACE_URI, "schema");
		for (Method method : methods) {
			if (isAccessible(method)) {
				createElement(method, root);
			}
		}
		XmlUtil.createFile(document, "file.xsd");
	}

	private static void createElement(Method method, Element root) throws Exception {
		Class<?>[] parameterTypes = method.getParameterTypes();
		Annotation[][] parameterAnnotations = method.getParameterAnnotations();
		Element rootElement = XmlUtil.addElementNamespace(root, NAMESPACE_ALIAS, NAMESPACE_URI, "element");
		XmlUtil.setAttribute(rootElement, "name", method.getName());
		Element complexType = XmlUtil.addElementNamespace(rootElement, NAMESPACE_ALIAS, NAMESPACE_URI, "complexType");
		for (int i = 0; i < parameterTypes.length; ++i) {
			Parameter parameter = getParameter(parameterAnnotations[i]);
			String type = getDataType(parameterTypes[i]);
			Element attribute = XmlUtil.addElementNamespace(complexType, NAMESPACE_ALIAS, NAMESPACE_URI, "attribute");
			XmlUtil.setAttribute(attribute, "name", parameter.name());
			XmlUtil.setAttribute(attribute, "type", String.format("%s:%s", NAMESPACE_ALIAS, type));
			if (parameter.required()) {
				XmlUtil.setAttribute(attribute, "use", "required");
			}
		}
	}

	public static boolean isAccessible(Member member) {
		return Modifier.isPublic(member.getModifiers()) && Modifier.isPublic(member.getDeclaringClass().getModifiers());
	}

	private static Parameter getParameter(Annotation[] annotations) throws MissingAnnotationException {
		for (Annotation annotation : annotations) {
			if (annotation instanceof Parameter) {
				return (Parameter) annotation;
			}
		}
		throw new MissingAnnotationException();
	}

	private static String getDataType(Class<?> clazz) throws Exception {
		for (SupportedType supportedType : SupportedType.values()) {
			if (supportedType.compare(clazz)) {
				return supportedType.getType();
			}
		}
		throw new UnsupportedDataTypeException();
	}
}
